from django.shortcuts import render
import json,random,string,logging
from django.views import View
from django_redis import get_redis_connection
# get_redis_connection是django中连接redis数据库的方法
from django.http import HttpResponse, JsonResponse
from . import constants
from .forms import CheckImgCodeForm
from users.models import Users
from utils.res_code import Code, error_map
from utils.yuntongxun.sms import CCP
from utils.json_fun import to_json_data
from utils.captcha.captcha import captcha
# 安装图片验证码所需要的 Pillow 模块：pip install Pillow

# Create your views here.

# 导入日志器
logger = logging.getLogger('django')
# logging.getLogger('在settings中设置的日志器的名称')


class ImageCode(View): # 生成图形验证码返回给前端
    """
    define image verification view
    # /image_codes/<uuid:image_code_id>/
    """
    def get(self, request, image_code_id):
        # 使用captcha中的generate_captcha()方法生成图形验证码
        text, image = captcha.generate_captcha()
        # 把生成的图形验证码的值赋给变量

        con_redis = get_redis_connection(alias='verify_codes')
        # get_redis_connection(alias='redis数据库的名称')
        # 确保settings.py文件中有配置redis CACHE
        # Redis原生指令参考 http://redisdoc.com/index.html
        # Redis python客户端 方法参考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables

        img_key = "img_{}".format(image_code_id)
        # 把图形验证码的UUID作为键 图形验证码文本内容作为值

        # 将图片验证码的key和验证码文本保存到redis中，并设置过期时间
        con_redis.setex(img_key, constants.IMAGE_CODE_REDIS_EXPIRES, text)
        # con_redis.setex(键,存在时间,值) redis数据库的增加方法

        logger.info("Image code: {}".format(text)) # 日志器的打印信息

        return HttpResponse(content=image, content_type="image/jpg")
        # content_type="image/jpg" content_type指定content值的类型


class CheckUsernameView(View):
    """
    Check whether the user exists：检查用户的存在
    get的请求方式  url：usernames/(?P<username>\w{5,20})/
    使用re_path设置正则 查看url中传递的参数是否匹配正则
    """
    # 思路 请求方式 传递参数(用户名) url路由传参 re_path匹配校验参数
    def get(self,request,username):
        count = Users.objects.filter(username=username).count()
        # 统计数据库中username参数传递的数据在数据库中的个数
        data = {
            'username': username, # 使我们re_path中传递的username
            'count': count,
        }
        return to_json_data(data=data)
    # 把数据传入到我们定义好的json格式中 返回给前端页面


class CheckMobileView(View):
    """
    Check the existence of mobile phone number：检查手机号的存在
    get的请求方式  url：mobiles/(?P<mobile>1[3-9]\d{9})/
    使用re_path设置正则 查看url中传递的参数是否匹配正则
    """
    # 思路  请求方式 传递参数(用户名) url路由传参 re_path匹配校验参数
    def get(self,request,mobile):
        count = Users.objects.filter(mobile=mobile).count()
        # 统计数据库中mobile参数传递的数据在数据库中的个数
        data = {
            'mobile': mobile, # 使我们re_path中传递的moblie
            'count': count,
        }
        return to_json_data(data=data)
    # 把数据传入到我们定义好的json格式中 返回给前端页面


class SmsCodesView(View):
    """send mobile sms code：发送手机短信验证码  post请求  url：/sms_codes/"""
    # 思路 1.发送的参数 手机号 图形验证码 图形验证码的UUID
    # 2.请求方式 POST  3.提交参数的方式 ajax提交 4.获取数据 校验数据
    # 校验：1.手机:不为空,手机号格式是否正确,手机号是否注册过
    # 2.验证码:不为空,与数据库中存入的数据一样  3.uuid:格式正确

    def post(self,request):# 为什么用post请求 因为有手机号 验证码文本 UUID等隐私敏感信息
        # 步骤 1.获取参数
        json_data = request.body
        # print(type(json_data),id(json_data),1)
        # print(json_data,2)
        # 拿到ajax中的数据 是byte类型的字符串数据 decode解码后为str
        if not json_data: # 判断json_data是否接收到ajax传来的数据
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data=json.loads(json_data.decode('utf-8')) # 把json数据转换为dict
        # print(type(dict_data),id(dict_data),3)
        # print(dict_data,4)

        # 2.验证参数 我们用form表单验证
        form = CheckImgCodeForm(data=dict_data) # 实例化 传入数据
        # print(type(form),id(form),5)
        if form.is_valid():
            # print(type(form.cleaned_data), id(form.cleaned_data),13)
            # print(form.cleaned_data,14)
            # 3.将短信验证码保存到redis数据库中 获取手机号
            mobile = form.cleaned_data.get('mobile')
            # cleaned_data 就是读取表单返回的值，返回类型为字典dict型

            # 创建短信验证码内容 random模块生成随机数 string模块digits属性 数字类型的字符串
            sms_num = ''.join([random.choice(string.digits) for _ in range(constants.SMS_CODE_NUMS)])
            # 列表推导式 for_inrange中的_相当于在占位符

            # get_redis_connection方法 连接redis数据库
            redis_conn = get_redis_connection(alias='verify_codes')
            pl = redis_conn.pipeline() # 使用redis中的管道(pipeline)来批量操作数据

            # 创建一个在60s以内是否有发送短信记录的标记
            sms_flag_fmt = "sms_flag_{}".format(mobile)
            # 创建保存短信验证码的标记 键(key)
            sms_text_fmt = "sms_{}".format(mobile)

            # 此处设置为True会出现bug
            try: # 管道(pipeline)来批量操作数据 可能会出错所以要使用异常处理 try
                pl.setex(sms_flag_fmt, constants.SEND_SMS_CODE_INTERVAL, 1)
                # 管道使用 setex(键,存在时间,值) 把数据存入redis数据库中
                pl.setex(sms_text_fmt, constants.SMS_CODE_REDIS_EXPIRES, sms_num)
                pl.execute() # 让管道通知redis执行命令的方法
            except Exception as e: # 异常处理
                logger.debug("redis 执行出现异常：{}".format(e))
                return to_json_data(errno=Code.UNKOWNERR, errmsg=error_map[Code.UNKOWNERR])

            logger.info("Sms code: {}".format(sms_num))

            # 4.发送短语验证码   这里我们直接使用这两行代码打印日志和告诉前端发送成功就行
            # 打印日志          至于原因 没有那么多手机号可以用来做测试
            logger.info("发送验证码短信[正常][ mobile: %s sms_code: %s]" % (mobile, sms_num))
            # 5.返回给前端
            return to_json_data(errno=Code.OK, errmsg="短信验证码发送成功")
            # try:
            #     # send_template_sms('手机号', [验证码, 时间], 内容模板id)
            # # 时间单位是分钟 如果时间是浮点数那么短信就会写 3.0分钟这样的字样 int转换一下就好
            #     result = CCP().send_template_sms(mobile,
            #                      [sms_num, constants.SMS_CODE_EXPIRES],
            #                                      constants.SMS_CODE_TEMP_ID)
            # except Exception as e:
            #     logger.error("发送验证码短信[异常][ mobile: %s, message: %s ]" % (mobile, e))
            #     return to_json_data(errno=Code.SMSERROR, errmsg=error_map[Code.SMSERROR])
            # else:
            #     if result == 0:
            #         # 打印日志
            #         logger.info("发送验证码短信[正常][ mobile: %s sms_code: %s]" % (mobile, sms_num))
            #         # 5.返回给前端
            #         return to_json_data(errno=Code.OK, errmsg="短信验证码发送成功")
            #     else:
            #         # 打印日志
            #         logger.warning("发送验证码短信[失败][ mobile: %s ]" % mobile)
            #         # 5.返回给前端
            #         return to_json_data(errno=Code.SMSFAIL, errmsg=error_map[Code.SMSFAIL])

        else: # form表单form.is_valid()验证出错的时候 定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():
                # item 的得到的是一个list类型的数据 切片得到list中的字典数据在get去对应的值
                err_msg_list.append(item[0].get('message'))
                # print(item[0].get('message'))   # for test 做测试用的 在js里有
            err_msg_str = '/'.join(err_msg_list)  # 拼接错误信息为一个字符串

            return to_json_data(errno=Code.PARAMERR, errmsg=err_msg_str)